• Load necessary libraries

library(ggplot2)
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(plotly)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
library(scales)

• Load the dataset

df <- read.csv("Forest/forest.csv")
head(df)

• Check for any null values

any(is.na(df))
[1] FALSE

Data Cleaning

• Combine all the soil types into a single column

# Since there are 40 of the columns dedicated only to soil type, it was necessary to reduce those columns into one
df <- df %>%
  mutate(Combined_Soil_Type = case_when(
    Soil_Type1 == 1 ~ "1",
    Soil_Type2 == 1 ~ "2",
    Soil_Type3 == 1 ~ "3",
    Soil_Type4 == 1 ~ "4",
    Soil_Type5 == 1 ~ "5",
    Soil_Type6 == 1 ~ "6",
    Soil_Type7 == 1 ~ "7",
    Soil_Type8 == 1 ~ "8",
    Soil_Type9 == 1 ~ "9",
    Soil_Type10 == 1 ~ "10",
    Soil_Type11 == 1 ~ "11",
    Soil_Type12 == 1 ~ "12",
    Soil_Type13 == 1 ~ "13",
    Soil_Type14 == 1 ~ "14",
    Soil_Type15 == 1 ~ "15",
    Soil_Type16 == 1 ~ "16",
    Soil_Type17 == 1 ~ "17",
    Soil_Type18 == 1 ~ "18",
    Soil_Type19 == 1 ~ "19",
    Soil_Type20 == 1 ~ "20",
    Soil_Type21 == 1 ~ "21",
    Soil_Type22 == 1 ~ "22",
    Soil_Type23 == 1 ~ "23",
    Soil_Type24 == 1 ~ "24",
    Soil_Type25 == 1 ~ "25",
    Soil_Type26 == 1 ~ "26",
    Soil_Type27 == 1 ~ "27",
    Soil_Type28 == 1 ~ "28",
    Soil_Type29 == 1 ~ "29",
    Soil_Type30 == 1 ~ "30",
    Soil_Type31 == 1 ~ "31",
    Soil_Type32 == 1 ~ "32",
    Soil_Type33 == 1 ~ "33",
    Soil_Type34 == 1 ~ "34",
    Soil_Type35 == 1 ~ "35",
    Soil_Type36 == 1 ~ "36",
    Soil_Type37 == 1 ~ "37",
    Soil_Type38 == 1 ~ "38",
    Soil_Type39 == 1 ~ "39",
    Soil_Type40 == 1 ~ "40"
  ))

• Doing the same for wilderness area

df <- df %>%
  mutate(Combined_Wilderness_Area = case_when(
    Wilderness_Area1 == 1 ~ "Rawah Area",
    Wilderness_Area2 == 1 ~ "Neota Area",
    Wilderness_Area3 == 1 ~ "Comanche Peak Area",
    Wilderness_Area4 == 1 ~ "Cache la Poudre Area"
  ))

• Summing up all hillshade into one

df <- df %>%
  mutate(Total_Hillshade = Hillshade_9am + Hillshade_Noon + Hillshade_3pm)

• Change degrees into cardinal directions

# Function to convert Aspect into cardinal and intermediate directions
get_cardinal_direction_full <- function(aspect) {
  if (!is.na(aspect) && aspect >= 0 && aspect < 360) {
    if (aspect >= 337.5 || aspect < 22.5) {
      return("N")
    } else if (aspect >= 22.5 && aspect < 67.5) {
      return("NE")
    } else if (aspect >= 67.5 && aspect < 112.5) {
      return("E")
    } else if (aspect >= 112.5 && aspect < 157.5) {
      return("SE")
    } else if (aspect >= 157.5 && aspect < 202.5) {
      return("S")
    } else if (aspect >= 202.5 && aspect < 247.5) {
      return("SW")
    } else if (aspect >= 247.5 && aspect < 292.5) {
      return("W")
    } else if (aspect >= 292.5 && aspect < 337.5) {
      return("NW")
    }
  }
  return("N") 
}

# Apply the function to the Aspect column directly
df$Aspect <- sapply(df$Aspect, get_cardinal_direction_full)

# View the first few rows to confirm
head(df$Aspect)
[1] "NE" "NE" "SE" "SE" "NE" "SE"

• Set the values for cover type

cover_type_mapping <- c(
  `1` = "Spruce/Fir",
  `2` = "Lodgepole Pine",
  `3` = "Ponderosa Pine",
  `4` = "Cottonwood/Willow",
  `5` = "Aspen",
  `6` = "Douglas-fir",
  `7` = "Krummholz"
)

# Replace the Cover_Type values in the dataframe
df$Cover_Type <- factor(cover_type_mapping[as.character(df$Cover_Type)], levels = cover_type_mapping)

# View the first few rows to confirm the changes
head(df$Cover_Type)
[1] Aspen          Aspen          Lodgepole Pine Lodgepole Pine Aspen          Lodgepole Pine
Levels: Spruce/Fir Lodgepole Pine Ponderosa Pine Cottonwood/Willow Aspen Douglas-fir Krummholz

• Since the Slope ranges from 0 to 50, we can make it into continuous value so that while plotting it makes it easier and not very messy

# Modify the Slope column to group values in bins of 5
df$Slope <- cut(df$Slope, 
                breaks = seq(0, max(df$Slope, na.rm = TRUE), by = 5), 
                include.lowest = TRUE, 
                right = FALSE, 
                labels = paste(seq(0, max(df$Slope, na.rm = TRUE) - 5, by = 5), 
                               seq(5, max(df$Slope, na.rm = TRUE), by = 5), sep = "-"))

• Check distribution for Elevation

a <- ggplot(df, aes(Elevation)) +
  geom_histogram(fill="steelblue")+
  labs(title= "Distribution of Elevvation")
a

a+facet_wrap(Combined_Wilderness_Area~.) +
   labs(title = "Wilderness Areas across different elevation levels")

ggplot(df, aes(Elevation)) +
  geom_histogram(fill="steelblue")+
  labs(title= "Distribution of Elevation") +
  facet_wrap(Aspect~.)

ggplot(df, aes(x = Elevation)) +
  geom_density(fill = "blue", alpha = 0.5) +
  labs(title = "Density Plot of Elevation", x = "Elevation", y = "Density") +
  theme_minimal()

• Since our data set is too large for proper visualization, we will take a small sample of the data that resembles the trend of the original data.

set.seed(123)  # Setting a seed for reproducibility
df_sample <- df %>% sample_n(3000)
ggplot(df_sample, aes(x = Elevation)) +
  geom_density(fill = "blue", alpha = 0.5) +
  labs(title = "Density Plot of Elevation", x = "Elevation", y = "Density") +
  theme_minimal()

• Check the distribution of Slope

ggplot(df, aes(Slope)) +
  geom_bar()

ggplot(df, aes(x = Slope)) +
  geom_density(fill = "blue", alpha = 0.5) +
  labs(title = "Density Plot of Slope", x = "Slope", y = "Density") +
  theme_minimal()

ggplot(df_sample, aes(x = Slope)) +
  geom_density(fill = "blue", alpha = 0.5) +
  labs(title = "Density Plot of Slope", x = "Slope", y = "Density") +
  theme_minimal()

Since the density of slope and elevation looks similar to that of the real dataset, we will conclude that other feature will follow the same trend.

• Distribution of Cover Type throughout the data set

a<-ggplot(df, aes(Cover_Type)) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  scale_y_continuous(labels = scales::comma)+
  geom_bar(fill="steelblue")+
  geom_text(stat = 'count', aes(label = ..count..), 
            vjust = -0.5, color = "black") +
  labs(title = "Distribution of Cover Type",
       x="Cover Type",
       y = "Count")
a

• Facet the cover distribution according to Wilderness Area

a + facet_grid(Combined_Wilderness_Area~., scales="free")

a+facet_wrap(Aspect~.)

• Check how much sunlight does each direction receive at 9am

ggplot(df, aes(Hillshade_9am))+
  geom_histogram(fill="steelblue") +
facet_wrap(Aspect~.) +
  labs(title = "Hillshade at 9am")

East and North received the most sunlight at 9am.

• Check how much sunlight does each direction receive during noon

ggplot(df, aes(Hillshade_Noon))+
  geom_histogram(fill="steelblue") +
  facet_wrap(Aspect~.) +
  labs(title = "Hillshade during Noon")

During the noon time, only south west and west received the least amount of sunlight.

• Check how much sunlight does each direction receive at 3pm

ggplot(df, aes(Hillshade_3pm))+
  geom_histogram(fill="steelblue") +
  facet_wrap(Aspect~.) +
  labs(title = "Hillshade at 3pm")

From all the plots above, it is clear that west and southwest received the least amount of sunlight.

• Remove the unnecessary columns Since our data set already had a massive set of columns and the columns ‘Soil_Type’ and ‘Wilderness_Area’ have been categorized into the column ‘Combined_Soil_Type’ and ‘Combined_Wilderness_Area’ they are unnecessary

df <- df %>%
  select(-starts_with("Soil_Type"), -starts_with("Wilderness_Area"), -starts_with("Hillshade")) #Remove any column that starts with the word Soil_Type, Wilderness_Area and Hillshade

• Checking to see if the changes have been applied

df
a+facet_wrap(.~Combined_Wilderness_Area)

• Plot density graph for elevation vs cover type

ggplot(df, aes(x = Elevation, fill = factor(Cover_Type))) +
  geom_density(alpha = 0.5) +
  labs(title = "Density of Elevation by Cover Type", x = "Elevation", y = "Density")

We can see that Cottonwood grows at lower altitude and Krummholz grows at the higher altitude.

ggplot(df_sample, aes(x=Cover_Type, y=Elevation))+geom_boxplot()

• Check distribution for soil type

p<-ggplot(df, aes(x = Combined_Soil_Type)) +
  geom_bar(fill = "steelblue") +
  labs(title = "Distribution of Combined Soil Types", x = "Soil Type", y = "Count") +
  theme(axis.text.x = element_text(angle = 0, hjust = 1))
p

From the bar plot, it can be seen that the most type of prevalent soil is of type 29 which is almost 1200000. On the other hand, Most of the soil types are almost non-existent like soil_Type 13,14,37 and many more which indicates their presence in somewhat small amount compared to others.

• Facet soil type distribution by wilderness area

p+facet_wrap(Combined_Wilderness_Area~.) +
  labs(title = "Distribution of Soil Type across Wilderness Areas")

Rawah Area receives has the most amount of soils while Comanache Area has the most variety of soils.

•

ggplot(df, aes(x = Cover_Type, y = Total_Hillshade, fill = Cover_Type)) +
  geom_boxplot() +
  labs(title = "Total Hillshade (Sunlight) Distribution by Cover Type",
       x = "Cover Type",
       y = "Total Hillshade (Sunlight)") + scale_y_continuous(labels = scales::comma) +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) + facet_grid(Combined_Wilderness_Area~.)

From the graph above, it seems that every type of plant can grow when receiving more sunlight but Lodgepole Pine can grow on areas with low sunlight of upto only 200 units.

• Create a bar for soil type and stack it with cover type

ggplot(df, aes(x = Combined_Soil_Type, fill = Cover_Type)) +
  geom_bar(position = "stack") +
  labs(title = "Cover Type and Soil Type", x = "Soil Type", y = "Count") +
  theme(axis.text.x = element_text(angle = 0, hjust = 1))

From the bar plot we can see that soil 29 is the most suitable for spruce/fir tree and Lodgepole Pine tree which are also the most present type of trees on other soils compared to other plants indicating their capacity to grow on any type of situation.

• Facet it with Wilderness Area

ggplot(df, aes(x = Combined_Soil_Type, fill = Cover_Type)) +
  geom_bar(position = "stack") +
  labs(title = "Cover Type and Soil Type", x = "Soil Type", y = "Count") +
  facet_wrap(Combined_Wilderness_Area~.)+
  theme(axis.text.x = element_text(angle = 0, hjust = 1))

• Check to see if the relation between soil type and hydrology

b<-ggplot(df, aes(x=Horizontal_Distance_To_Hydrology, y=Vertical_Distance_To_Hydrology)) + geom_smooth() + facet_wrap(~Combined_Soil_Type, scales="free") +
  labs(title = "Type of Soil available near water bodies")

From the plots it is hard to determine relation between the two as every soil type seem to be available at almost every horizontal distance away from a body of water. Some like type 1, 16, 18, 2, 21, 8, 9, 5, 6, 7 is found in the range of 600m away from the body of water, the other types are available 1000 meters away. As for the vertical distance from hydrology, most are found at the range of 100-150 meters above and some like 8 and 25 are found below the body of water. Soil type 4, 24, 27, 28, 31, 32, 38, 39 and 40 could be found above 200m above any body of water but soil type like 7, 8, 9 and 25 are found as close as 40 meter and even potrayed in negative indicating them being submerged in the body of water

• Add new feature ‘Cover_Type’ as colour

ggplot(df_sample, aes(x=Horizontal_Distance_To_Hydrology, y=Vertical_Distance_To_Hydrology, colour = Cover_Type)) + geom_point() + facet_wrap(~Combined_Soil_Type, scales="free") +
  labs(title = "Cover type and sources of water")

• Point graph with Elevation vs Soil Type

ggplot(df_sample, aes(y=Elevation, x=Combined_Soil_Type, colour = Cover_Type))+
  geom_point()+
  labs(title = "Elevation vs Soil Type")

As the type of soil increases so does the elevation.

•


# Create a boxplot
ggplot(df_sample, aes(x = Elevation, y = Total_Hillshade, colour = Cover_Type)) +
  geom_point(alpha = 0.5) +
  labs(title = "Sunlight Exposure (Hillshade) vs Elevation",
       x = "Elevation",
       y = "Total Hillshade (Sunlight)") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  facet_grid(Combined_Wilderness_Area~.)

• Check the

ggplot(df_sample, aes(x = Elevation, y = Total_Hillshade)) +
  geom_point(alpha = 0.5) +
  labs(title = "Sunlight Exposure (Hillshade) vs Elevation",
       x = "Elevation",
       y = "Total Hillshade (Sunlight)") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

•

ggplot(df, aes(y=Total_Hillshade,x =Cover_Type, fill = Cover_Type)) +
  geom_boxplot() +
  theme(axis.text.x = element_text(angle = 45))

• Plot a histogram forhillshade and facet it with wilderness area and cover type

ggplot(df, aes(x = Total_Hillshade)) +
  geom_histogram(position = "dodge", binwidth = 10, fill="steelblue") +
  labs(title = "Distribution of Cover_Type", x = "Hill Shade(Sunlight)", y = "Count") +
  theme(axis.text.x = element_text( hjust = 1)) + 
  facet_grid(Cover_Type~Combined_Wilderness_Area, scales = "free")

From the plot, we can see that some wilderness area is unsuitable for some type of forest as they do not grow there at all. Eg, Spruce and …… do not grow on wilderness area 4 at all but ….wood only grows on wilderness area 4. If we were to look at the plot alone it would seem like Wilderness_Area4 hosts the most forest but if we look at the count then we can see that Wilderness_Area1 hosts the most forest. If we were to determine the conditions of the Wilderness Area then we can determine the type of forest that grows on such terrains.

q <- ggplot(df, aes(x = Combined_Wilderness_Area, fill = Cover_Type)) +
  geom_bar(position = "stack") +
  labs(title = "Distribution of Cover Type according to Wilderness Area",
       x = "Wilderness Area",
       y = "Count") +
  geom_text(stat = 'count', aes(label = ..count..), position = position_stack(vjust = 0.5), size = 3, color = "white") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
q

ggplotly(q)

From the bar graph, we can see that the most prevalent type of forest is Lodgepole Pine followed by Spruce/Fir which are at an noticeable amount followed by Ponderosa Pine and so on. We can also see that the Rawah Area hosts the most number of plants while the Comanche Peak Area hosts the most type of plants.

• Create a point plot for elevation vs horizontal distance to roadways

a<-ggplot(df_sample, aes(y=Elevation, x=Horizontal_Distance_To_Roadways, size = Slope, colour = Cover_Type)) + geom_point(alpha = 0.5) + labs(title = "Distribution of Cover types near roadways")
a

• Facet the above plot by wilderness area

a+facet_wrap(Combined_Wilderness_Area~., scales = "free")

ggplotly(a)
Warning: Using size for a discrete variable is not advised.

• Create a histogram for Horizontal_Distance_To_Fire_Points and facet it with Cover_Type and Combined_Wilderness_Area

ggplot(df_sample, aes(Horizontal_Distance_To_Fire_Points)) + geom_histogram() +
  facet_grid(Combined_Wilderness_Area~Cover_Type) +
  labs(title="Areas prone to forest fire")

From the graph above, it seems that Rawah Area and Comanache Peak Area are prone to wild fires. Specially species of plant like Spruce and Lodgepole Pine are planted near those areas rather than any other species.

ggplot(df_sample, aes(x=Horizontal_Distance_To_Fire_Points,y=Horizontal_Distance_To_Hydrology, colour = Cover_Type)) + geom_point() +
  facet_wrap(.~Combined_Wilderness_Area)

ggplot(df_sample, aes(x=Horizontal_Distance_To_Roadways, y=Horizontal_Distance_To_Fire_Points, colour = Cover_Type)) +
  geom_point() +facet_wrap(Combined_Wilderness_Area~.)

From the graph it can be seen that in Cache la Poudre Area is closer to Roadways and Fireplaces but the plants are at a scare amount nut in Neota Area, Spruce/Fir are closest to roadways and fire points but also at an scarce amount. On the other hand, in Comanche Peak Area Spruce/Fir, Lodgepole pine are present at a high amount and are closer to roadways and fire point but Aspen and Douglas fir like plants are planted further awat from those points. The same could be said for the Rawah Area except for this area Spruce/Fir and Aspen are planted close to fire points.

ggplot(df_sample, aes(x=Horizontal_Distance_To_Hydrology, y=Horizontal_Distance_To_Fire_Points)) +
  geom_point()

https://roh4n.shinyapps.io/Rohan_Kalu_23189640/

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmF1dGhvcjogUm9oYW4gS2FsdQ0KLS0tDQoNCuKAoiBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShzY2FsZXMpDQpgYGANCg0KDQrigKIgTG9hZCB0aGUgZGF0YXNldA0KYGBge3J9DQpkZiA8LSByZWFkLmNzdigiRm9yZXN0L2ZvcmVzdC5jc3YiKQ0KaGVhZChkZikNCmBgYA0KDQrigKIgQ2hlY2sgZm9yIGFueSBudWxsIHZhbHVlcw0KYGBge3J9DQphbnkoaXMubmEoZGYpKQ0KYGBgDQoNCiMgRGF0YSBDbGVhbmluZw0K4oCiIENvbWJpbmUgYWxsIHRoZSBzb2lsIHR5cGVzIGludG8gYSBzaW5nbGUgY29sdW1uDQpgYGB7cn0NCiMgU2luY2UgdGhlcmUgYXJlIDQwIG9mIHRoZSBjb2x1bW5zIGRlZGljYXRlZCBvbmx5IHRvIHNvaWwgdHlwZSwgaXQgd2FzIG5lY2Vzc2FyeSB0byByZWR1Y2UgdGhvc2UgY29sdW1ucyBpbnRvIG9uZQ0KZGYgPC0gZGYgJT4lDQogIG11dGF0ZShDb21iaW5lZF9Tb2lsX1R5cGUgPSBjYXNlX3doZW4oDQogICAgU29pbF9UeXBlMSA9PSAxIH4gIjEiLA0KICAgIFNvaWxfVHlwZTIgPT0gMSB+ICIyIiwNCiAgICBTb2lsX1R5cGUzID09IDEgfiAiMyIsDQogICAgU29pbF9UeXBlNCA9PSAxIH4gIjQiLA0KICAgIFNvaWxfVHlwZTUgPT0gMSB+ICI1IiwNCiAgICBTb2lsX1R5cGU2ID09IDEgfiAiNiIsDQogICAgU29pbF9UeXBlNyA9PSAxIH4gIjciLA0KICAgIFNvaWxfVHlwZTggPT0gMSB+ICI4IiwNCiAgICBTb2lsX1R5cGU5ID09IDEgfiAiOSIsDQogICAgU29pbF9UeXBlMTAgPT0gMSB+ICIxMCIsDQogICAgU29pbF9UeXBlMTEgPT0gMSB+ICIxMSIsDQogICAgU29pbF9UeXBlMTIgPT0gMSB+ICIxMiIsDQogICAgU29pbF9UeXBlMTMgPT0gMSB+ICIxMyIsDQogICAgU29pbF9UeXBlMTQgPT0gMSB+ICIxNCIsDQogICAgU29pbF9UeXBlMTUgPT0gMSB+ICIxNSIsDQogICAgU29pbF9UeXBlMTYgPT0gMSB+ICIxNiIsDQogICAgU29pbF9UeXBlMTcgPT0gMSB+ICIxNyIsDQogICAgU29pbF9UeXBlMTggPT0gMSB+ICIxOCIsDQogICAgU29pbF9UeXBlMTkgPT0gMSB+ICIxOSIsDQogICAgU29pbF9UeXBlMjAgPT0gMSB+ICIyMCIsDQogICAgU29pbF9UeXBlMjEgPT0gMSB+ICIyMSIsDQogICAgU29pbF9UeXBlMjIgPT0gMSB+ICIyMiIsDQogICAgU29pbF9UeXBlMjMgPT0gMSB+ICIyMyIsDQogICAgU29pbF9UeXBlMjQgPT0gMSB+ICIyNCIsDQogICAgU29pbF9UeXBlMjUgPT0gMSB+ICIyNSIsDQogICAgU29pbF9UeXBlMjYgPT0gMSB+ICIyNiIsDQogICAgU29pbF9UeXBlMjcgPT0gMSB+ICIyNyIsDQogICAgU29pbF9UeXBlMjggPT0gMSB+ICIyOCIsDQogICAgU29pbF9UeXBlMjkgPT0gMSB+ICIyOSIsDQogICAgU29pbF9UeXBlMzAgPT0gMSB+ICIzMCIsDQogICAgU29pbF9UeXBlMzEgPT0gMSB+ICIzMSIsDQogICAgU29pbF9UeXBlMzIgPT0gMSB+ICIzMiIsDQogICAgU29pbF9UeXBlMzMgPT0gMSB+ICIzMyIsDQogICAgU29pbF9UeXBlMzQgPT0gMSB+ICIzNCIsDQogICAgU29pbF9UeXBlMzUgPT0gMSB+ICIzNSIsDQogICAgU29pbF9UeXBlMzYgPT0gMSB+ICIzNiIsDQogICAgU29pbF9UeXBlMzcgPT0gMSB+ICIzNyIsDQogICAgU29pbF9UeXBlMzggPT0gMSB+ICIzOCIsDQogICAgU29pbF9UeXBlMzkgPT0gMSB+ICIzOSIsDQogICAgU29pbF9UeXBlNDAgPT0gMSB+ICI0MCINCiAgKSkNCmBgYA0KDQrigKIgRG9pbmcgdGhlIHNhbWUgZm9yIHdpbGRlcm5lc3MgYXJlYQ0KYGBge3J9DQpkZiA8LSBkZiAlPiUNCiAgbXV0YXRlKENvbWJpbmVkX1dpbGRlcm5lc3NfQXJlYSA9IGNhc2Vfd2hlbigNCiAgICBXaWxkZXJuZXNzX0FyZWExID09IDEgfiAiUmF3YWggQXJlYSIsDQogICAgV2lsZGVybmVzc19BcmVhMiA9PSAxIH4gIk5lb3RhIEFyZWEiLA0KICAgIFdpbGRlcm5lc3NfQXJlYTMgPT0gMSB+ICJDb21hbmNoZSBQZWFrIEFyZWEiLA0KICAgIFdpbGRlcm5lc3NfQXJlYTQgPT0gMSB+ICJDYWNoZSBsYSBQb3VkcmUgQXJlYSINCiAgKSkNCmBgYA0KDQoNCuKAoiBTdW1taW5nIHVwIGFsbCBoaWxsc2hhZGUgaW50byBvbmUNCmBgYHtyfQ0KZGYgPC0gZGYgJT4lDQogIG11dGF0ZShUb3RhbF9IaWxsc2hhZGUgPSBIaWxsc2hhZGVfOWFtICsgSGlsbHNoYWRlX05vb24gKyBIaWxsc2hhZGVfM3BtKQ0KYGBgDQoNCg0K4oCiIENoYW5nZSBkZWdyZWVzIGludG8gY2FyZGluYWwgZGlyZWN0aW9ucw0KYGBge3J9DQojIEZ1bmN0aW9uIHRvIGNvbnZlcnQgQXNwZWN0IGludG8gY2FyZGluYWwgYW5kIGludGVybWVkaWF0ZSBkaXJlY3Rpb25zDQpnZXRfY2FyZGluYWxfZGlyZWN0aW9uX2Z1bGwgPC0gZnVuY3Rpb24oYXNwZWN0KSB7DQogIGlmICghaXMubmEoYXNwZWN0KSAmJiBhc3BlY3QgPj0gMCAmJiBhc3BlY3QgPCAzNjApIHsNCiAgICBpZiAoYXNwZWN0ID49IDMzNy41IHx8IGFzcGVjdCA8IDIyLjUpIHsNCiAgICAgIHJldHVybigiTiIpDQogICAgfSBlbHNlIGlmIChhc3BlY3QgPj0gMjIuNSAmJiBhc3BlY3QgPCA2Ny41KSB7DQogICAgICByZXR1cm4oIk5FIikNCiAgICB9IGVsc2UgaWYgKGFzcGVjdCA+PSA2Ny41ICYmIGFzcGVjdCA8IDExMi41KSB7DQogICAgICByZXR1cm4oIkUiKQ0KICAgIH0gZWxzZSBpZiAoYXNwZWN0ID49IDExMi41ICYmIGFzcGVjdCA8IDE1Ny41KSB7DQogICAgICByZXR1cm4oIlNFIikNCiAgICB9IGVsc2UgaWYgKGFzcGVjdCA+PSAxNTcuNSAmJiBhc3BlY3QgPCAyMDIuNSkgew0KICAgICAgcmV0dXJuKCJTIikNCiAgICB9IGVsc2UgaWYgKGFzcGVjdCA+PSAyMDIuNSAmJiBhc3BlY3QgPCAyNDcuNSkgew0KICAgICAgcmV0dXJuKCJTVyIpDQogICAgfSBlbHNlIGlmIChhc3BlY3QgPj0gMjQ3LjUgJiYgYXNwZWN0IDwgMjkyLjUpIHsNCiAgICAgIHJldHVybigiVyIpDQogICAgfSBlbHNlIGlmIChhc3BlY3QgPj0gMjkyLjUgJiYgYXNwZWN0IDwgMzM3LjUpIHsNCiAgICAgIHJldHVybigiTlciKQ0KICAgIH0NCiAgfQ0KICByZXR1cm4oIk4iKSANCn0NCg0KIyBBcHBseSB0aGUgZnVuY3Rpb24gdG8gdGhlIEFzcGVjdCBjb2x1bW4gZGlyZWN0bHkNCmRmJEFzcGVjdCA8LSBzYXBwbHkoZGYkQXNwZWN0LCBnZXRfY2FyZGluYWxfZGlyZWN0aW9uX2Z1bGwpDQoNCiMgVmlldyB0aGUgZmlyc3QgZmV3IHJvd3MgdG8gY29uZmlybQ0KaGVhZChkZiRBc3BlY3QpDQoNCmBgYA0KDQrigKIgU2V0IHRoZSB2YWx1ZXMgZm9yIGNvdmVyIHR5cGUNCmBgYHtyfQ0KY292ZXJfdHlwZV9tYXBwaW5nIDwtIGMoDQogIGAxYCA9ICJTcHJ1Y2UvRmlyIiwNCiAgYDJgID0gIkxvZGdlcG9sZSBQaW5lIiwNCiAgYDNgID0gIlBvbmRlcm9zYSBQaW5lIiwNCiAgYDRgID0gIkNvdHRvbndvb2QvV2lsbG93IiwNCiAgYDVgID0gIkFzcGVuIiwNCiAgYDZgID0gIkRvdWdsYXMtZmlyIiwNCiAgYDdgID0gIktydW1taG9seiINCikNCg0KIyBSZXBsYWNlIHRoZSBDb3Zlcl9UeXBlIHZhbHVlcyBpbiB0aGUgZGF0YWZyYW1lDQpkZiRDb3Zlcl9UeXBlIDwtIGZhY3Rvcihjb3Zlcl90eXBlX21hcHBpbmdbYXMuY2hhcmFjdGVyKGRmJENvdmVyX1R5cGUpXSwgbGV2ZWxzID0gY292ZXJfdHlwZV9tYXBwaW5nKQ0KDQojIFZpZXcgdGhlIGZpcnN0IGZldyByb3dzIHRvIGNvbmZpcm0gdGhlIGNoYW5nZXMNCmhlYWQoZGYkQ292ZXJfVHlwZSkNCg0KYGBgDQoNCuKAoiBTaW5jZSB0aGUgU2xvcGUgcmFuZ2VzIGZyb20gMCB0byA1MCwgd2UgY2FuIG1ha2UgaXQgaW50byBjb250aW51b3VzIHZhbHVlIHNvIHRoYXQgd2hpbGUgcGxvdHRpbmcgaXQgbWFrZXMgaXQgZWFzaWVyIGFuZCBub3QgdmVyeSBtZXNzeQ0KYGBge3J9DQojIE1vZGlmeSB0aGUgU2xvcGUgY29sdW1uIHRvIGdyb3VwIHZhbHVlcyBpbiBiaW5zIG9mIDUNCmRmJFNsb3BlIDwtIGN1dChkZiRTbG9wZSwgDQogICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIG1heChkZiRTbG9wZSwgbmEucm0gPSBUUlVFKSwgYnkgPSA1KSwgDQogICAgICAgICAgICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFLCANCiAgICAgICAgICAgICAgICByaWdodCA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICBsYWJlbHMgPSBwYXN0ZShzZXEoMCwgbWF4KGRmJFNsb3BlLCBuYS5ybSA9IFRSVUUpIC0gNSwgYnkgPSA1KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VxKDUsIG1heChkZiRTbG9wZSwgbmEucm0gPSBUUlVFKSwgYnkgPSA1KSwgc2VwID0gIi0iKSkNCmBgYA0KDQoNCuKAoiBDaGVjayBkaXN0cmlidXRpb24gZm9yIEVsZXZhdGlvbg0KYGBge3J9DQphIDwtIGdncGxvdChkZiwgYWVzKEVsZXZhdGlvbikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0ic3RlZWxibHVlIikrDQogIGxhYnModGl0bGU9ICJEaXN0cmlidXRpb24gb2YgRWxldnZhdGlvbiIpDQphDQpgYGANCg0KYGBge3J9DQphK2ZhY2V0X3dyYXAoQ29tYmluZWRfV2lsZGVybmVzc19BcmVhfi4pICsNCiAgIGxhYnModGl0bGUgPSAiV2lsZGVybmVzcyBBcmVhcyBhY3Jvc3MgZGlmZmVyZW50IGVsZXZhdGlvbiBsZXZlbHMiKQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGYsIGFlcyhFbGV2YXRpb24pKSArDQogIGdlb21faGlzdG9ncmFtKGZpbGw9InN0ZWVsYmx1ZSIpKw0KICBsYWJzKHRpdGxlPSAiRGlzdHJpYnV0aW9uIG9mIEVsZXZhdGlvbiIpICsNCiAgZmFjZXRfd3JhcChBc3BlY3R+LikNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRmLCBhZXMoeCA9IEVsZXZhdGlvbikpICsNCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArDQogIGxhYnModGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIEVsZXZhdGlvbiIsIHggPSAiRWxldmF0aW9uIiwgeSA9ICJEZW5zaXR5IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCuKAoiBTaW5jZSBvdXIgZGF0YSBzZXQgaXMgdG9vIGxhcmdlIGZvciBwcm9wZXIgdmlzdWFsaXphdGlvbiwgd2Ugd2lsbCB0YWtlIGEgc21hbGwgc2FtcGxlIG9mIHRoZSBkYXRhIHRoYXQgcmVzZW1ibGVzIHRoZSB0cmVuZCBvZiB0aGUgb3JpZ2luYWwgZGF0YS4NCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKSAgIyBTZXR0aW5nIGEgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQpkZl9zYW1wbGUgPC0gZGYgJT4lIHNhbXBsZV9uKDMwMDApDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGZfc2FtcGxlLCBhZXMoeCA9IEVsZXZhdGlvbikpICsNCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArDQogIGxhYnModGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIEVsZXZhdGlvbiIsIHggPSAiRWxldmF0aW9uIiwgeSA9ICJEZW5zaXR5IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCg0K4oCiIENoZWNrIHRoZSBkaXN0cmlidXRpb24gb2YgU2xvcGUNCmBgYHtyfQ0KZ2dwbG90KGRmLCBhZXMoU2xvcGUpKSArDQogIGdlb21fYmFyKCkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHggPSBTbG9wZSkpICsNCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArDQogIGxhYnModGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIFNsb3BlIiwgeCA9ICJTbG9wZSIsIHkgPSAiRGVuc2l0eSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkZl9zYW1wbGUsIGFlcyh4ID0gU2xvcGUpKSArDQogIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgUGxvdCBvZiBTbG9wZSIsIHggPSAiU2xvcGUiLCB5ID0gIkRlbnNpdHkiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANClNpbmNlIHRoZSBkZW5zaXR5IG9mIHNsb3BlIGFuZCBlbGV2YXRpb24gbG9va3Mgc2ltaWxhciB0byB0aGF0IG9mIHRoZSByZWFsIGRhdGFzZXQsIHdlIHdpbGwgY29uY2x1ZGUgdGhhdCBvdGhlciBmZWF0dXJlIHdpbGwgZm9sbG93IHRoZSBzYW1lIHRyZW5kLg0KDQoNCuKAoiBEaXN0cmlidXRpb24gb2YgQ292ZXIgVHlwZSB0aHJvdWdob3V0IHRoZSBkYXRhIHNldA0KYGBge3J9DQphPC1nZ3Bsb3QoZGYsIGFlcyhDb3Zlcl9UeXBlKSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSsNCiAgZ2VvbV9iYXIoZmlsbD0ic3RlZWxibHVlIikrDQogIGdlb21fdGV4dChzdGF0ID0gJ2NvdW50JywgYWVzKGxhYmVsID0gLi5jb3VudC4uKSwgDQogICAgICAgICAgICB2anVzdCA9IC0wLjUsIGNvbG9yID0gImJsYWNrIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBDb3ZlciBUeXBlIiwNCiAgICAgICB4PSJDb3ZlciBUeXBlIiwNCiAgICAgICB5ID0gIkNvdW50IikNCmENCmBgYA0KDQoNCuKAoiBGYWNldCB0aGUgY292ZXIgZGlzdHJpYnV0aW9uIGFjY29yZGluZyB0byBXaWxkZXJuZXNzIEFyZWENCmBgYHtyfQ0KYSArIGZhY2V0X2dyaWQoQ29tYmluZWRfV2lsZGVybmVzc19BcmVhfi4sIHNjYWxlcz0iZnJlZSIpDQpgYGANCg0KYGBge3J9DQphK2ZhY2V0X3dyYXAoQXNwZWN0fi4pDQpgYGANCg0K4oCiIENoZWNrIGhvdyBtdWNoIHN1bmxpZ2h0IGRvZXMgZWFjaCBkaXJlY3Rpb24gcmVjZWl2ZSBhdCA5YW0NCmBgYHtyfQ0KZ2dwbG90KGRmLCBhZXMoSGlsbHNoYWRlXzlhbSkpKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsPSJzdGVlbGJsdWUiKSArDQpmYWNldF93cmFwKEFzcGVjdH4uKSArDQogIGxhYnModGl0bGUgPSAiSGlsbHNoYWRlIGF0IDlhbSIpDQpgYGANCkVhc3QgYW5kIE5vcnRoIHJlY2VpdmVkIHRoZSBtb3N0IHN1bmxpZ2h0IGF0IDlhbS4NCg0KDQrigKIgQ2hlY2sgaG93IG11Y2ggc3VubGlnaHQgZG9lcyBlYWNoIGRpcmVjdGlvbiByZWNlaXZlIGR1cmluZyBub29uDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKEhpbGxzaGFkZV9Ob29uKSkrDQogIGdlb21faGlzdG9ncmFtKGZpbGw9InN0ZWVsYmx1ZSIpICsNCiAgZmFjZXRfd3JhcChBc3BlY3R+LikgKw0KICBsYWJzKHRpdGxlID0gIkhpbGxzaGFkZSBkdXJpbmcgTm9vbiIpDQpgYGANCkR1cmluZyB0aGUgbm9vbiB0aW1lLCBvbmx5IHNvdXRoIHdlc3QgYW5kIHdlc3QgcmVjZWl2ZWQgdGhlIGxlYXN0IGFtb3VudCBvZiBzdW5saWdodC4NCg0KDQrigKIgQ2hlY2sgaG93IG11Y2ggc3VubGlnaHQgZG9lcyBlYWNoIGRpcmVjdGlvbiByZWNlaXZlIGF0IDNwbQ0KYGBge3J9DQpnZ3Bsb3QoZGYsIGFlcyhIaWxsc2hhZGVfM3BtKSkrDQogIGdlb21faGlzdG9ncmFtKGZpbGw9InN0ZWVsYmx1ZSIpICsNCiAgZmFjZXRfd3JhcChBc3BlY3R+LikgKw0KICBsYWJzKHRpdGxlID0gIkhpbGxzaGFkZSBhdCAzcG0iKQ0KYGBgDQpGcm9tIGFsbCB0aGUgcGxvdHMgYWJvdmUsIGl0IGlzIGNsZWFyIHRoYXQgd2VzdCBhbmQgc291dGh3ZXN0IHJlY2VpdmVkIHRoZSBsZWFzdCBhbW91bnQgb2Ygc3VubGlnaHQuDQoNCg0KDQrigKIgUmVtb3ZlIHRoZSB1bm5lY2Vzc2FyeSBjb2x1bW5zDQpTaW5jZSBvdXIgZGF0YSBzZXQgYWxyZWFkeSBoYWQgYSBtYXNzaXZlIHNldCBvZiBjb2x1bW5zIGFuZCB0aGUgY29sdW1ucyAnU29pbF9UeXBlJyBhbmQgJ1dpbGRlcm5lc3NfQXJlYScgaGF2ZSBiZWVuIGNhdGVnb3JpemVkIGludG8gdGhlIGNvbHVtbiAnQ29tYmluZWRfU29pbF9UeXBlJyBhbmQgJ0NvbWJpbmVkX1dpbGRlcm5lc3NfQXJlYScgdGhleSBhcmUgdW5uZWNlc3NhcnkNCmBgYHtyfQ0KZGYgPC0gZGYgJT4lDQogIHNlbGVjdCgtc3RhcnRzX3dpdGgoIlNvaWxfVHlwZSIpLCAtc3RhcnRzX3dpdGgoIldpbGRlcm5lc3NfQXJlYSIpLCAtc3RhcnRzX3dpdGgoIkhpbGxzaGFkZSIpKSAjUmVtb3ZlIGFueSBjb2x1bW4gdGhhdCBzdGFydHMgd2l0aCB0aGUgd29yZCBTb2lsX1R5cGUsIFdpbGRlcm5lc3NfQXJlYSBhbmQgSGlsbHNoYWRlDQpgYGANCg0K4oCiIENoZWNraW5nIHRvIHNlZSBpZiB0aGUgY2hhbmdlcyBoYXZlIGJlZW4gYXBwbGllZA0KYGBge3J9DQpkZg0KYGBgDQoNCg0KYGBge3J9DQphK2ZhY2V0X3dyYXAoLn5Db21iaW5lZF9XaWxkZXJuZXNzX0FyZWEpDQpgYGANCg0KDQoNCuKAoiBQbG90IGRlbnNpdHkgZ3JhcGggZm9yIGVsZXZhdGlvbiB2cyBjb3ZlciB0eXBlDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHggPSBFbGV2YXRpb24sIGZpbGwgPSBmYWN0b3IoQ292ZXJfVHlwZSkpKSArDQogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNSkgKw0KICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgb2YgRWxldmF0aW9uIGJ5IENvdmVyIFR5cGUiLCB4ID0gIkVsZXZhdGlvbiIsIHkgPSAiRGVuc2l0eSIpDQoNCmBgYA0KV2UgY2FuIHNlZSB0aGF0IENvdHRvbndvb2QgZ3Jvd3MgYXQgbG93ZXIgYWx0aXR1ZGUgYW5kIEtydW1taG9seiBncm93cyBhdCB0aGUgaGlnaGVyIGFsdGl0dWRlLg0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRmX3NhbXBsZSwgYWVzKHg9Q292ZXJfVHlwZSwgeT1FbGV2YXRpb24pKStnZW9tX2JveHBsb3QoKQ0KYGBgDQoNCg0K4oCiIENoZWNrIGRpc3RyaWJ1dGlvbiBmb3Igc29pbCB0eXBlDQpgYGB7cn0NCnA8LWdncGxvdChkZiwgYWVzKHggPSBDb21iaW5lZF9Tb2lsX1R5cGUpKSArDQogIGdlb21fYmFyKGZpbGwgPSAic3RlZWxibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBDb21iaW5lZCBTb2lsIFR5cGVzIiwgeCA9ICJTb2lsIFR5cGUiLCB5ID0gIkNvdW50IikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSkpDQpwDQpgYGANCkZyb20gdGhlIGJhciBwbG90LCBpdCBjYW4gYmUgc2VlbiB0aGF0IHRoZSBtb3N0IHR5cGUgb2YgcHJldmFsZW50IHNvaWwgaXMgb2YgdHlwZSAyOSB3aGljaCBpcyBhbG1vc3QgMTIwMDAwMC4gT24gdGhlIG90aGVyIGhhbmQsIE1vc3Qgb2YgdGhlIHNvaWwgdHlwZXMgYXJlIGFsbW9zdCBub24tZXhpc3RlbnQgbGlrZSBzb2lsX1R5cGUgMTMsMTQsMzcgYW5kIG1hbnkgbW9yZSB3aGljaCBpbmRpY2F0ZXMgdGhlaXIgcHJlc2VuY2UgaW4gc29tZXdoYXQgc21hbGwgYW1vdW50IGNvbXBhcmVkIHRvIG90aGVycy4NCg0KDQrigKIgRmFjZXQgc29pbCB0eXBlIGRpc3RyaWJ1dGlvbiBieSB3aWxkZXJuZXNzIGFyZWENCmBgYHtyfQ0KcCtmYWNldF93cmFwKENvbWJpbmVkX1dpbGRlcm5lc3NfQXJlYX4uKSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFNvaWwgVHlwZSBhY3Jvc3MgV2lsZGVybmVzcyBBcmVhcyIpDQpgYGANClJhd2FoIEFyZWEgcmVjZWl2ZXMgaGFzIHRoZSBtb3N0IGFtb3VudCBvZiBzb2lscyB3aGlsZSBDb21hbmFjaGUgQXJlYSBoYXMgdGhlIG1vc3QgdmFyaWV0eSBvZiBzb2lscy4NCg0KDQrigKIgDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHggPSBDb3Zlcl9UeXBlLCB5ID0gVG90YWxfSGlsbHNoYWRlLCBmaWxsID0gQ292ZXJfVHlwZSkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBsYWJzKHRpdGxlID0gIlRvdGFsIEhpbGxzaGFkZSAoU3VubGlnaHQpIERpc3RyaWJ1dGlvbiBieSBDb3ZlciBUeXBlIiwNCiAgICAgICB4ID0gIkNvdmVyIFR5cGUiLA0KICAgICAgIHkgPSAiVG90YWwgSGlsbHNoYWRlIChTdW5saWdodCkiKSArIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsgZmFjZXRfZ3JpZChDb21iaW5lZF9XaWxkZXJuZXNzX0FyZWF+LikNCg0KYGBgDQpGcm9tIHRoZSBncmFwaCBhYm92ZSwgaXQgc2VlbXMgdGhhdCBldmVyeSB0eXBlIG9mIHBsYW50IGNhbiBncm93IHdoZW4gcmVjZWl2aW5nIG1vcmUgc3VubGlnaHQgYnV0IExvZGdlcG9sZSBQaW5lIGNhbiBncm93IG9uIGFyZWFzIHdpdGggbG93IHN1bmxpZ2h0IG9mIHVwdG8gb25seSAyMDAgdW5pdHMuDQoNCg0K4oCiIENyZWF0ZSBhIGJhciBmb3Igc29pbCB0eXBlIGFuZCBzdGFjayBpdCB3aXRoIGNvdmVyIHR5cGUNCmBgYHtyfQ0KZ2dwbG90KGRmLCBhZXMoeCA9IENvbWJpbmVkX1NvaWxfVHlwZSwgZmlsbCA9IENvdmVyX1R5cGUpKSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIikgKw0KICBsYWJzKHRpdGxlID0gIkNvdmVyIFR5cGUgYW5kIFNvaWwgVHlwZSIsIHggPSAiU29pbCBUeXBlIiwgeSA9ICJDb3VudCIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEpKQ0KYGBgDQpGcm9tIHRoZSBiYXIgcGxvdCB3ZSBjYW4gc2VlIHRoYXQgc29pbCAyOSBpcyB0aGUgbW9zdCBzdWl0YWJsZSBmb3Igc3BydWNlL2ZpciB0cmVlIGFuZCBMb2RnZXBvbGUgUGluZSB0cmVlIHdoaWNoIGFyZSBhbHNvIHRoZSBtb3N0IHByZXNlbnQgdHlwZSBvZiB0cmVlcyBvbiBvdGhlciBzb2lscyBjb21wYXJlZCB0byBvdGhlciBwbGFudHMgaW5kaWNhdGluZyB0aGVpciBjYXBhY2l0eSB0byBncm93IG9uIGFueSB0eXBlIG9mIHNpdHVhdGlvbi4NCg0KDQrigKIgIEZhY2V0IGl0IHdpdGggV2lsZGVybmVzcyBBcmVhDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHggPSBDb21iaW5lZF9Tb2lsX1R5cGUsIGZpbGwgPSBDb3Zlcl9UeXBlKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgbGFicyh0aXRsZSA9ICJDb3ZlciBUeXBlIGFuZCBTb2lsIFR5cGUiLCB4ID0gIlNvaWwgVHlwZSIsIHkgPSAiQ291bnQiKSArDQogIGZhY2V0X3dyYXAoQ29tYmluZWRfV2lsZGVybmVzc19BcmVhfi4pKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSkpDQpgYGANCg0KDQrigKIgQ2hlY2sgdG8gc2VlIGlmIHRoZSByZWxhdGlvbiBiZXR3ZWVuIHNvaWwgdHlwZSBhbmQgaHlkcm9sb2d5DQpgYGB7cn0NCmI8LWdncGxvdChkZiwgYWVzKHg9SG9yaXpvbnRhbF9EaXN0YW5jZV9Ub19IeWRyb2xvZ3ksIHk9VmVydGljYWxfRGlzdGFuY2VfVG9fSHlkcm9sb2d5KSkgKyBnZW9tX3Ntb290aCgpICsgZmFjZXRfd3JhcCh+Q29tYmluZWRfU29pbF9UeXBlLCBzY2FsZXM9ImZyZWUiKSArDQogIGxhYnModGl0bGUgPSAiVHlwZSBvZiBTb2lsIGF2YWlsYWJsZSBuZWFyIHdhdGVyIGJvZGllcyIpDQpgYGANCkZyb20gdGhlIHBsb3RzIGl0IGlzIGhhcmQgdG8gZGV0ZXJtaW5lIHJlbGF0aW9uIGJldHdlZW4gdGhlIHR3byBhcyBldmVyeSBzb2lsIHR5cGUgc2VlbSB0byBiZSBhdmFpbGFibGUgYXQgYWxtb3N0IGV2ZXJ5IGhvcml6b250YWwgZGlzdGFuY2UgYXdheSBmcm9tIGEgYm9keSBvZiB3YXRlci4gU29tZSBsaWtlIHR5cGUgMSwgMTYsIDE4LCAyLCAyMSwgOCwgOSwgNSwgNiwgNyBpcyBmb3VuZCBpbiB0aGUgcmFuZ2Ugb2YgNjAwbSBhd2F5IGZyb20gdGhlIGJvZHkgb2Ygd2F0ZXIsIHRoZSBvdGhlciB0eXBlcyBhcmUgYXZhaWxhYmxlIDEwMDAgbWV0ZXJzIGF3YXkuIA0KQXMgZm9yIHRoZSB2ZXJ0aWNhbCBkaXN0YW5jZSBmcm9tIGh5ZHJvbG9neSwgbW9zdCBhcmUgZm91bmQgYXQgdGhlIHJhbmdlIG9mIDEwMC0xNTAgbWV0ZXJzIGFib3ZlIGFuZCBzb21lIGxpa2UgOCBhbmQgMjUgYXJlIGZvdW5kIGJlbG93IHRoZSBib2R5IG9mIHdhdGVyLiBTb2lsIHR5cGUgNCwgMjQsIDI3LCAyOCwgMzEsIDMyLCAzOCwgMzkgYW5kIDQwIGNvdWxkIGJlIGZvdW5kIGFib3ZlIDIwMG0gYWJvdmUgYW55IGJvZHkgb2Ygd2F0ZXIgYnV0IHNvaWwgdHlwZSBsaWtlIDcsIDgsIDkgYW5kIDI1IGFyZSBmb3VuZCBhcyBjbG9zZSBhcyA0MCBtZXRlciBhbmQgZXZlbiBwb3RyYXllZCBpbiBuZWdhdGl2ZSBpbmRpY2F0aW5nIHRoZW0gYmVpbmcgc3VibWVyZ2VkIGluIHRoZSBib2R5IG9mIHdhdGVyDQoNCg0K4oCiIEFkZCBuZXcgZmVhdHVyZSAnQ292ZXJfVHlwZScgYXMgY29sb3VyDQpgYGB7cn0NCmdncGxvdChkZl9zYW1wbGUsIGFlcyh4PUhvcml6b250YWxfRGlzdGFuY2VfVG9fSHlkcm9sb2d5LCB5PVZlcnRpY2FsX0Rpc3RhbmNlX1RvX0h5ZHJvbG9neSwgY29sb3VyID0gQ292ZXJfVHlwZSkpICsgZ2VvbV9wb2ludCgpICsgZmFjZXRfd3JhcCh+Q29tYmluZWRfU29pbF9UeXBlLCBzY2FsZXM9ImZyZWUiKSArDQogIGxhYnModGl0bGUgPSAiQ292ZXIgdHlwZSBhbmQgc291cmNlcyBvZiB3YXRlciIpDQpgYGANCg0K4oCiIFBvaW50IGdyYXBoIHdpdGggRWxldmF0aW9uIHZzIFNvaWwgVHlwZQ0KYGBge3J9DQpnZ3Bsb3QoZGZfc2FtcGxlLCBhZXMoeT1FbGV2YXRpb24sIHg9Q29tYmluZWRfU29pbF9UeXBlLCBjb2xvdXIgPSBDb3Zlcl9UeXBlKSkrDQogIGdlb21fcG9pbnQoKSsNCiAgbGFicyh0aXRsZSA9ICJFbGV2YXRpb24gdnMgU29pbCBUeXBlIikNCmBgYA0KQXMgdGhlIHR5cGUgb2Ygc29pbCBpbmNyZWFzZXMgc28gZG9lcyB0aGUgZWxldmF0aW9uLg0KDQrigKINCmBgYHtyfQ0KDQojIENyZWF0ZSBhIGJveHBsb3QNCmdncGxvdChkZl9zYW1wbGUsIGFlcyh4ID0gRWxldmF0aW9uLCB5ID0gVG90YWxfSGlsbHNoYWRlLCBjb2xvdXIgPSBDb3Zlcl9UeXBlKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArDQogIGxhYnModGl0bGUgPSAiU3VubGlnaHQgRXhwb3N1cmUgKEhpbGxzaGFkZSkgdnMgRWxldmF0aW9uIiwNCiAgICAgICB4ID0gIkVsZXZhdGlvbiIsDQogICAgICAgeSA9ICJUb3RhbCBIaWxsc2hhZGUgKFN1bmxpZ2h0KSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKw0KICBmYWNldF9ncmlkKENvbWJpbmVkX1dpbGRlcm5lc3NfQXJlYX4uKQ0KDQpgYGANCg0K4oCiIENoZWNrIHRoZSANCmBgYHtyfQ0KZ2dwbG90KGRmX3NhbXBsZSwgYWVzKHggPSBFbGV2YXRpb24sIHkgPSBUb3RhbF9IaWxsc2hhZGUpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsNCiAgbGFicyh0aXRsZSA9ICJTdW5saWdodCBFeHBvc3VyZSAoSGlsbHNoYWRlKSB2cyBFbGV2YXRpb24iLA0KICAgICAgIHggPSAiRWxldmF0aW9uIiwNCiAgICAgICB5ID0gIlRvdGFsIEhpbGxzaGFkZSAoU3VubGlnaHQpIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQ0KYGBgDQoNCg0K4oCiDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHk9VG90YWxfSGlsbHNoYWRlLHggPUNvdmVyX1R5cGUsIGZpbGwgPSBDb3Zlcl9UeXBlKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUpKQ0KYGBgDQoNCg0KDQrigKIgUGxvdCBhIGhpc3RvZ3JhbSBmb3JoaWxsc2hhZGUgYW5kIGZhY2V0IGl0IHdpdGggd2lsZGVybmVzcyBhcmVhIGFuZCBjb3ZlciB0eXBlDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHggPSBUb3RhbF9IaWxsc2hhZGUpKSArDQogIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uID0gImRvZGdlIiwgYmlud2lkdGggPSAxMCwgZmlsbD0ic3RlZWxibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBDb3Zlcl9UeXBlIiwgeCA9ICJIaWxsIFNoYWRlKFN1bmxpZ2h0KSIsIHkgPSAiQ291bnQiKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KCBoanVzdCA9IDEpKSArIA0KICBmYWNldF9ncmlkKENvdmVyX1R5cGV+Q29tYmluZWRfV2lsZGVybmVzc19BcmVhLCBzY2FsZXMgPSAiZnJlZSIpDQoNCmBgYA0KRnJvbSB0aGUgcGxvdCwgd2UgY2FuIHNlZSB0aGF0IHNvbWUgd2lsZGVybmVzcyBhcmVhIGlzIHVuc3VpdGFibGUgZm9yIHNvbWUgdHlwZSBvZiBmb3Jlc3QgYXMgdGhleSBkbyBub3QgZ3JvdyB0aGVyZSBhdCBhbGwuIEVnLCBTcHJ1Y2UgYW5kICAuLi4uLi4gZG8gbm90IGdyb3cgb24gd2lsZGVybmVzcyBhcmVhIDQgYXQgYWxsIGJ1dCAuLi4ud29vZCBvbmx5IGdyb3dzIG9uIHdpbGRlcm5lc3MgYXJlYSA0Lg0KSWYgd2Ugd2VyZSB0byBsb29rIGF0IHRoZSBwbG90IGFsb25lIGl0IHdvdWxkIHNlZW0gbGlrZSBXaWxkZXJuZXNzX0FyZWE0IGhvc3RzIHRoZSBtb3N0IGZvcmVzdCBidXQgaWYgd2UgbG9vayBhdCB0aGUgY291bnQgdGhlbiB3ZSBjYW4gc2VlIHRoYXQgV2lsZGVybmVzc19BcmVhMSBob3N0cyB0aGUgbW9zdCBmb3Jlc3QuDQpJZiB3ZSB3ZXJlIHRvIGRldGVybWluZSB0aGUgY29uZGl0aW9ucyBvZiB0aGUgV2lsZGVybmVzcyBBcmVhIHRoZW4gd2UgY2FuIGRldGVybWluZSB0aGUgdHlwZSBvZiBmb3Jlc3QgdGhhdCBncm93cyBvbiBzdWNoIHRlcnJhaW5zLg0KDQoNCg0KYGBge3J9DQpxIDwtIGdncGxvdChkZiwgYWVzKHggPSBDb21iaW5lZF9XaWxkZXJuZXNzX0FyZWEsIGZpbGwgPSBDb3Zlcl9UeXBlKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgQ292ZXIgVHlwZSBhY2NvcmRpbmcgdG8gV2lsZGVybmVzcyBBcmVhIiwNCiAgICAgICB4ID0gIldpbGRlcm5lc3MgQXJlYSIsDQogICAgICAgeSA9ICJDb3VudCIpICsNCiAgZ2VvbV90ZXh0KHN0YXQgPSAnY291bnQnLCBhZXMobGFiZWwgPSAuLmNvdW50Li4pLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgc2l6ZSA9IDMsIGNvbG9yID0gIndoaXRlIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KcQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90bHkocSkNCmBgYA0KRnJvbSB0aGUgYmFyIGdyYXBoLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIG1vc3QgcHJldmFsZW50IHR5cGUgb2YgZm9yZXN0IGlzIExvZGdlcG9sZSBQaW5lIGZvbGxvd2VkIGJ5IFNwcnVjZS9GaXIgd2hpY2ggYXJlIGF0IGFuIG5vdGljZWFibGUgYW1vdW50IGZvbGxvd2VkIGJ5IFBvbmRlcm9zYSBQaW5lIGFuZCBzbyBvbi4gV2UgY2FuIGFsc28gc2VlIHRoYXQgdGhlIFJhd2FoIEFyZWEgaG9zdHMgdGhlIG1vc3QgbnVtYmVyIG9mIHBsYW50cyB3aGlsZSB0aGUgQ29tYW5jaGUgUGVhayBBcmVhIGhvc3RzIHRoZSBtb3N0IHR5cGUgb2YgcGxhbnRzLg0KDQoNCuKAoiBDcmVhdGUgYSBwb2ludCBwbG90IGZvciBlbGV2YXRpb24gdnMgaG9yaXpvbnRhbCBkaXN0YW5jZSB0byByb2Fkd2F5cyANCmBgYHtyfQ0KYTwtZ2dwbG90KGRmX3NhbXBsZSwgYWVzKHk9RWxldmF0aW9uLCB4PUhvcml6b250YWxfRGlzdGFuY2VfVG9fUm9hZHdheXMsIHNpemUgPSBTbG9wZSwgY29sb3VyID0gQ292ZXJfVHlwZSkpICsgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKyBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBDb3ZlciB0eXBlcyBuZWFyIHJvYWR3YXlzIikNCmENCmBgYA0K4oCiIEZhY2V0IHRoZSBhYm92ZSBwbG90IGJ5IHdpbGRlcm5lc3MgYXJlYQ0KYGBge3J9DQphK2ZhY2V0X3dyYXAoQ29tYmluZWRfV2lsZGVybmVzc19BcmVhfi4sIHNjYWxlcyA9ICJmcmVlIikNCmBgYA0KDQpgYGB7cn0NCmdncGxvdGx5KGEpDQpgYGANCg0KDQrigKIgQ3JlYXRlIGEgaGlzdG9ncmFtIGZvciBIb3Jpem9udGFsX0Rpc3RhbmNlX1RvX0ZpcmVfUG9pbnRzIGFuZCBmYWNldCBpdCB3aXRoIENvdmVyX1R5cGUgYW5kIENvbWJpbmVkX1dpbGRlcm5lc3NfQXJlYQ0KYGBge3J9DQpnZ3Bsb3QoZGZfc2FtcGxlLCBhZXMoSG9yaXpvbnRhbF9EaXN0YW5jZV9Ub19GaXJlX1BvaW50cykpICsgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGZhY2V0X2dyaWQoQ29tYmluZWRfV2lsZGVybmVzc19BcmVhfkNvdmVyX1R5cGUpICsNCiAgbGFicyh0aXRsZT0iQXJlYXMgcHJvbmUgdG8gZm9yZXN0IGZpcmUiKQ0KYGBgDQpGcm9tIHRoZSBncmFwaCBhYm92ZSwgaXQgc2VlbXMgdGhhdCBSYXdhaCBBcmVhIGFuZCBDb21hbmFjaGUgUGVhayBBcmVhIGFyZSBwcm9uZSB0byB3aWxkIGZpcmVzLiBTcGVjaWFsbHkgc3BlY2llcyBvZiBwbGFudCBsaWtlIFNwcnVjZSBhbmQgTG9kZ2Vwb2xlIFBpbmUgYXJlIHBsYW50ZWQgbmVhciB0aG9zZSBhcmVhcyByYXRoZXIgdGhhbiBhbnkgb3RoZXIgc3BlY2llcy4NCg0KDQpgYGB7cn0NCmdncGxvdChkZl9zYW1wbGUsIGFlcyh4PUhvcml6b250YWxfRGlzdGFuY2VfVG9fRmlyZV9Qb2ludHMseT1Ib3Jpem9udGFsX0Rpc3RhbmNlX1RvX0h5ZHJvbG9neSwgY29sb3VyID0gQ292ZXJfVHlwZSkpICsgZ2VvbV9wb2ludCgpICsNCiAgZmFjZXRfd3JhcCgufkNvbWJpbmVkX1dpbGRlcm5lc3NfQXJlYSkNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRmX3NhbXBsZSwgYWVzKHg9SG9yaXpvbnRhbF9EaXN0YW5jZV9Ub19Sb2Fkd2F5cywgeT1Ib3Jpem9udGFsX0Rpc3RhbmNlX1RvX0ZpcmVfUG9pbnRzLCBjb2xvdXIgPSBDb3Zlcl9UeXBlKSkgKw0KICBnZW9tX3BvaW50KCkgK2ZhY2V0X3dyYXAoQ29tYmluZWRfV2lsZGVybmVzc19BcmVhfi4pDQpgYGANCkZyb20gdGhlIGdyYXBoIGl0IGNhbiBiZSBzZWVuIHRoYXQgaW4gQ2FjaGUgbGEgUG91ZHJlIEFyZWEgaXMgY2xvc2VyIHRvIFJvYWR3YXlzIGFuZCBGaXJlcGxhY2VzIGJ1dCB0aGUgcGxhbnRzIGFyZSBhdCBhIHNjYXJlIGFtb3VudCBudXQgaW4gTmVvdGEgQXJlYSwgU3BydWNlL0ZpciBhcmUgY2xvc2VzdCB0byByb2Fkd2F5cyBhbmQgZmlyZSBwb2ludHMgYnV0IGFsc28gYXQgYW4gc2NhcmNlIGFtb3VudC4NCk9uIHRoZSBvdGhlciBoYW5kLCBpbiBDb21hbmNoZSBQZWFrIEFyZWEgU3BydWNlL0ZpciwgTG9kZ2Vwb2xlIHBpbmUgYXJlIHByZXNlbnQgYXQgYSBoaWdoIGFtb3VudCBhbmQgYXJlIGNsb3NlciB0byByb2Fkd2F5cyBhbmQgZmlyZSBwb2ludCBidXQgQXNwZW4gYW5kIERvdWdsYXMgZmlyIGxpa2UgcGxhbnRzIGFyZSBwbGFudGVkIGZ1cnRoZXIgYXdhdCBmcm9tIHRob3NlIHBvaW50cy4gVGhlIHNhbWUgY291bGQgYmUgc2FpZCBmb3IgdGhlIFJhd2FoIEFyZWEgZXhjZXB0IGZvciB0aGlzIGFyZWEgU3BydWNlL0ZpciBhbmQgQXNwZW4gYXJlIHBsYW50ZWQgY2xvc2UgdG8gZmlyZSBwb2ludHMuDQoNCg0KDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGZfc2FtcGxlLCBhZXMoeD1Ib3Jpem9udGFsX0Rpc3RhbmNlX1RvX0h5ZHJvbG9neSwgeT1Ib3Jpem9udGFsX0Rpc3RhbmNlX1RvX0ZpcmVfUG9pbnRzKSkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KDQpodHRwczovL3JvaDRuLnNoaW55YXBwcy5pby9Sb2hhbl9LYWx1XzIzMTg5NjQwLw0K